using System;
using Microsoft.SPOT;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer;
using Gadgeteer.Interfaces;

using Dairy_Town.Elm327.Core;
using System.Threading;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;

namespace Dairy_Town
{
    class OBD
    {
        ElmDriver elm;

        /// <summary>
        /// Every 12 seconds attempts to connect to the ECU
        /// </summary>
        GT.Timer poll_connection;

        private ElmDriver.ElmConnectionResultType connResult = ElmDriver.ElmConnectionResultType.NoConnectionToElm;

        /// <summary>
        /// Fired when the OBD module has successfully connected to the ECU
        /// </summary>
        public event EventHandler ObdConnected;

        /// <summary>
        /// Fired when the OBD module has disconnected from the ECU
        /// </summary>
        public event EventHandler ObdDisconnected;

        /// <summary>
        /// Returns true if OBD module is physically connected to vehicle. Does not determine connection with ECU
        /// </summary>
        public bool isConnected { get { return (connResult == ElmDriver.ElmConnectionResultType.NoConnectionToObd || connResult == ElmDriver.ElmConnectionResultType.Connected) ? true : false; } }

        /// <summary>
        /// Returns true if OBD module has connected to the ECU, typically means engine is running
        /// </summary>
        public bool isRunning { get { return (connResult == ElmDriver.ElmConnectionResultType.Connected) ? true : false; } }

        private double _Speed = 0.0;

        private double _Fuel = 0.0;

        //private double _Throttle = -1.0;

        //private double _RPM = -1.0;

        private Thread tPoll;

        /*
        public string SeatbeltFastened
        {
            get
            {
                return (this.isRunning) ? this.elm.ObdMode01.SeatbeltFastened.ToString() : "...";
            }
        }
        */

        /*
        public string Mileage
        {
            get
            {
                return (this.isRunning) ? this.elm.ObdMode01.Mileage.ToString() : "...";
            }
        }
        */

        
        public double FuelLevel
        {
            get
            {
                return this._Fuel;
            }
        }
        

        /*
        public string RPM
        {
            get
            {
                return this._RPM.ToString("F2");
            }
        }
        */

        /*
        public int RunningTime
        {
            get
            {
                return (this.isRunning) ? elm.ObdMode01.RunTimeSinceEngineStart : 0;
            }
        }
        */

        public double Speed
        {
            get
            {
                return this._Speed;
            }
        }

        /*
        public string Throttle
        {
            get
            {
                return this._Throttle.ToString("F2");
            }
        }
        */

        private static string _VIN = "...";
        public string VIN { get { return _VIN; } set { _VIN = value; } }

        public OBD(GTM.GHIElectronics.RS232 instance)
        {
            elm = new ElmDriver(instance);
            elm.ObdConnectionLost += elm_ObdConnectionLost;

            poll_connection = new GT.Timer(12000);
            poll_connection.Tick += poll_connection_Tick;
            poll_connection.Start();
        }

        public void Reset()
        {
            new Thread(() =>
                {
                    //Console.Display("Resetting OBD Connection");
                    connResult = ElmDriver.ElmConnectionResultType.NoConnectionToObd;
                    elm.Disconnect();

                    Thread.Sleep(500);

                    this.connect();
                }).Start();
        }

        private void connect()
        {
            try
            {
                Thread bg = new Thread(new ThreadStart(() =>
                    {
                        /// Thread is created in attempts to connect the OBD module to the ECU
                        connResult = elm.Connect();
                    }
                    ));
                bg.Start();
                
                string status = "";
                switch (connResult)
                {
                    case ElmDriver.ElmConnectionResultType.Connected: // OBD module is physically connected and has connected to the ECU
                        {
                            status = "OBD connected";
                            this.ObdConnected(this, EventArgs.Empty);
                            poll_connection.Stop(); // We no longer need to try and connect

                            tPoll = new Thread(new ThreadStart(poll_data_Tick)); // Open a new thread to poll the ECU for vehicle information
                            tPoll.Start();
                            Thread.Sleep(200);

                            _VIN = FormatVIN(elm.ObdMode09.VehicleIdentificationNumber);

                            break;
                        }
                    case ElmDriver.ElmConnectionResultType.NoConnectionToElm: // OBD module is not physically connected
                        {
                            status = "OBD port not connected";
                            break;
                        }
                    case ElmDriver.ElmConnectionResultType.NoConnectionToObd: // OBD module is only physically connected, but not connected to the ECU
                        {
                            status = "Vehicle is not active";
                            break;
                        }
                }

                Debug.Print(status);
            }
            catch (System.InvalidOperationException)
            {
                Debug.Print("Vehicle connection failed");
            }
        }

        void poll_data_Tick()
        {
            while(this.isRunning) // As long as the vehicle is running (ECU connected) let's get information
            {
                //Debug.Print("Seatbelt " + elm.ObdFord14.SeatbeltFastened);

                this._Speed = elm.ObdMode01.VehicleSpeed;
                //this._RPM = elm.ObdMode01.EngineRpm;
                //this._Throttle = elm.ObdMode01.ThrottlePosition;
                //this._Fuel = elm.ObdMode01.FuelLevel;

                Thread.Sleep(350); // Timeout before sending another data request
            }

            if (!this.isRunning)
                Thread.CurrentThread.Abort(); // Abort this operation if vehicle is not running (ECU disconnected)
        }

        /// <summary>
        /// Updates OBD connection status, alerts parent classes, and restarts the connection timer
        /// </summary>
        private void elm_ObdConnectionLost()
        {
            //Console.Display("OBD connection lost");
            connResult = ElmDriver.ElmConnectionResultType.NoConnectionToElm;
            if(this.ObdDisconnected != null)
                this.ObdDisconnected(this, EventArgs.Empty);
            poll_connection.Start();
        }

        void poll_connection_Tick(GT.Timer timer)
        {
            this.connect();
        }

        public string FormatVIN(string VIN)
        {
            Regex alphanumeric = new Regex("[^A-Z0-9]");
            Regex vinformat = new Regex("[IOQ]");
    
            VIN = alphanumeric.Replace(VIN, "");
            VIN = vinformat.Replace(VIN, "");

            return VIN;
        }
    }
}
